package com.chucang.shucang.common.base.redis;

import cn.hutool.core.text.CharSequenceUtil;
import com.chucang.shucang.common.base.exception.FastException;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;


import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * 缓存工具 目前只接入了redis
 *
 * @author wangkang
 * @email flitsneak@gmail.com
 * @date 2022/9/11 11:07
 */
@Slf4j
public class RedisUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

//-------------------------------------------common-------------------------------------------//

    /**
     * 判断是否有该键值
     *
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error("判断key:{}异常", key);
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    public void deleteKey(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(Arrays.asList(key));
            }
        }
    }

    /**
     * 批量删除key
     *
     * @param keyList keys
     */
    public void deleteKey(List<String> keyList) {
        try {
            log.info("批量删除keys========{}", keyList);
            redisTemplate.delete(keyList);
        } catch (Exception e) {
            log.error("批量删除keys异常，keys为{}", keyList);
        }

    }

    /**
     * 获取过期时间单位秒
     *
     * @param key
     * @return -1 表示没有设置过期时间 -2 代表没有该key 返回秒值
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param time
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            log.error("设置key:{}过期时间异常", key);
            return false;
        }
    }

    /**
     * 模糊查询key 禁用此方法 时间复杂度为O(n)!
     *
     * @param likeKey 模糊键
     * @return set
     */
    public Set<String> keys(String likeKey) {
        try {
            return redisTemplate.keys(likeKey);
        } catch (Exception e) {
            log.error("模糊查询keys失败，模糊查询key为{}", likeKey);
            return Collections.emptySet();
        }
    }


//-------------------------------------------String-------------------------------------------//

    /**
     * 获取对象
     *
     * @param key key
     * @return object
     */
    public Object getObject(String key) {
        try {
            return CharSequenceUtil.isBlank(key) ? null : redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            log.error("获取key为{}的对象失败！", key);
            log.error("失败详情为：", e);
            return null;
        }

    }

    /**
     * 批量获取对象
     *
     * @param keys
     * @return
     */
    public List<Object> multiGetObject(Collection<String> keys) {
        try {
            return redisTemplate.opsForValue().multiGet(keys);
        } catch (Exception e) {
            log.error("批量获取值错误，批量对象集合为{}", keys);
            return Collections.emptyList();
        }
    }

    /**
     * 存入对象
     *
     * @param key   key
     * @param value value
     * @return bool
     */
    public boolean setValue(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value, RedisCommonConstant.DEFAULT_TIME_TO_LIVE, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            log.error("存入key:{}值异常", key);
            return false;
        }
    }

    /**
     * 存入有过期时间的对象 单位秒
     *
     * @param key
     * @param value
     * @param time
     * @return
     */
    public boolean setValue(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else if (time == RedisCommonConstant.NO_EXPIRE_LIMIT) {
                this.setValue(key, value);
            } else {
                this.setValue(key, value);
            }
            return true;
        } catch (Exception e) {
            log.error("存入带过期时间的key:{}值异常", key);
            return false;
        }
    }

    /**
     * 存入数据
     *
     * @param key
     * @param value
     * @param time
     * @param timeUnit
     * @return
     */
    public boolean setValue(String key, Object value, long time, TimeUnit timeUnit) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, timeUnit);
            } else if (time == RedisCommonConstant.NO_EXPIRE_LIMIT) {
                this.setValue(key, value);
            } else {
                this.setValue(key, value);
            }
            return true;
        } catch (Exception e) {
            log.error("存入带过期时间的key:{}值异常", key);
            return false;
        }
    }

    /**
     * 原子递增
     *
     * @param key
     * @param delta
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            log.error("key:{}递增因子小于0");
            throw new FastException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 原子递减
     *
     * @param key
     * @param delta
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            log.error("key:{}递增因子小于0", key);
            throw new FastException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    public boolean setIfAbsent(String key, String value, long ttl, TimeUnit timeUnit) {
        try {
            return redisTemplate.opsForValue().setIfAbsent(key, value, ttl, timeUnit);
        } catch (Exception e) {
            log.error("占有锁时异常！异常key为:{}", key);
            return false;
        }
    }

//-------------------------------------------List-------------------------------------------//

    /**
     * 获取list缓存的内容
     *
     * @param key
     * @param start
     * @param end   0 到 -1 代表获取所有值
     * @return
     */
    public List<Object> getListValues(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            log.error("获取list值的key:{}异常", key);
            return Collections.emptyList();
        }
    }

    /**
     * 右侧弹出list元素
     *
     * @param key
     * @return
     */
    public Object popListValue(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 获取list长度
     *
     * @param key
     * @return
     */
    public long getListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            log.error("获取list长度的key:{}异常", key);
            return 0;
        }
    }

    /**
     * 根据索引获取list值
     *
     * @param key
     * @param index
     * @return
     */
    public Object getListIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            log.error("获取list指定index的key:{}异常", key);
            return null;
        }
    }

    /**
     * 左侧存list
     *
     * @param key
     * @param value
     * @return
     */
    public boolean pushListValues(String key, Object value) {
        try {
            redisTemplate.opsForList().leftPush(key, value);
            return true;
        } catch (Exception e) {
            log.error("弹出list的key:{}异常", key);
            return false;
        }
    }

    /**
     * 带入过期时间左侧存list
     *
     * @param key
     * @param value
     * @param time
     * @return
     */
    public boolean pushListValues(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().leftPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            log.error("存入list的key:{}异常", key);
            return false;
        }
    }

    /**
     * 批量存入list数据
     *
     * @param key
     * @param value
     * @return
     */
    public boolean pushListValues(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().leftPushAll(key, value);
            return true;
        } catch (Exception e) {
            log.error("批量存入list的key:{}异常", key);
            return false;
        }
    }

    /**
     * 批量带过期时间存入list
     *
     * @param key
     * @param value
     * @param time
     * @return
     */
    public boolean pushListValues(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().leftPushAll(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            log.error("带过期时间批量存入list的key:{}异常", key);
            return false;
        }
    }


    /**
     * 根据索引更新list的某个值
     *
     * @param key
     * @param index
     * @param value
     * @return
     */
    public boolean updateListIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            log.error("更新list指定index的key:{}异常", key);
            return false;
        }
    }

    /**
     * 移除count个值为value的数据
     *
     * @param key
     * @param count
     * @param value
     * @return
     */
    public long removeListValues(String key, long count, Object value) {
        try {
            return redisTemplate.opsForList().remove(key, count, value);
        } catch (Exception e) {
            log.error("删除list的key:{}异常", key);
            return 0;
        }
    }

    /**
     * pipeline 获取list值
     *
     * @param key
     * @param size
     * @return
     */
    public List<Integer> multiRPopPipeline(String key, int size) {
        int curSize = Math.toIntExact(redisTemplate.opsForList().size(key));
        if (curSize == 0) {
            return Collections.emptyList();
        }
        return redisTemplate.executePipelined(new SessionCallback<Integer>() {
                    @Override
                    public Integer execute(@NotNull RedisOperations redisOperations) throws DataAccessException {
                        final int finalSize = Math.toIntExact(Math.min(curSize, size));
                        for (int i = 0; i < finalSize; i++) {
                            redisOperations.opsForList().rightPop(key);
                        }
                        return null;
                    }
                })
                .stream()
                .filter(Objects::nonNull)
                .map(Integer.class::cast)
                .collect(Collectors.toList());
    }

//-------------------------------------------Hash-------------------------------------------//

    /**
     * 获取hash值
     *
     * @param key
     * @param item
     * @return
     */
    public Object getHashValue(String key, String item) {
        try {
            return redisTemplate.opsForHash().get(key, item);
        } catch (Exception e) {
            log.error("获取hash的key为{}，值为{}异常", key, item);
            log.error("异常信息为===》", e);
            return null;
        }
    }

    /**
     * 获取hashKey对应的所有键值
     *
     * @param key 键
     * @return
     */
    public Map<Object, Object> getHashValues(String key) {
        try {
            return redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            log.error("获取hash的key：{}，对应的值异常", key);
            return Collections.emptyMap();
        }
    }

    /**
     * Hash存入
     *
     * @param key
     * @param map
     * @return
     */
    public boolean setHashValues(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            log.error("批量保存hash的key:{}异常", key);
            return false;
        }
    }

    /**
     * hash过期时间
     *
     * @param key
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return
     */
    public boolean setHashValues(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            log.error("带过期时间批量保存hash的key:{}异常", key);
            return false;
        }
    }

    /**
     * 存入hash,如果不存在将创建
     *
     * @param key
     * @param item
     * @param value
     * @return
     */
    public boolean setHashValue(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            log.error("保存hash的key:{}异常", key);
            return false;
        }
    }

    /**
     * 存入hash,如果不存在将创建
     *
     * @param key
     * @param item
     * @param value
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return
     */
    public boolean setHashValue(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            log.error("带过期时间存入hash的key:{}异常", key);
            return false;
        }
    }

    /**
     * 删除hash值
     *
     * @param key  不能为null
     * @param item 可以使多个 不能为null
     */
    public void removeHash(String key, Object... item) {
        try {
            redisTemplate.opsForHash().delete(key, item);
        } catch (Exception e) {
            log.error("删除hash的key:{},item:{}异常", key, item);
        }
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  不能为null
     * @param item 不能为null
     * @return
     */
    public boolean hasHashKey(String key, String item) {
        try {
            return redisTemplate.opsForHash().hasKey(key, item);
        } catch (Exception e) {
            log.error("判断是否有hash的key：{}异常", key);
            return false;
        }
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key
     * @param item
     * @param by   要增加几(大于0)
     * @return
     */
    public double hashIncr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     *
     * @param key
     * @param item
     * @param by   要减少记(小于0)
     * @return double
     */
    public double hashDecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

//-------------------------------------------Set-------------------------------------------//

    /**
     * 根据key获取Set中的所有值
     *
     * @param key
     * @return
     */
    public Set<Object> getSetValue(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            log.error("获取set的key:{}异常", key);
            return Collections.emptySet();
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key
     * @param value
     * @return
     */
    public boolean hasSetKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            log.error("判断set的value的key:{}是否存在异常", key);
            return false;
        }
    }

    /**
     * 存入set
     *
     * @param key
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long setSetValues(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            log.error("批量保存hash的key:{}异常", key);
            return 0;
        }
    }

    /**
     * 存入set带过期时间
     *
     * @param key
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long setSetValues(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0) {
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            log.error("带过期时间存入set的key:{}异常", key);
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     *
     * @param key
     * @return
     */
    public long getSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            log.error("获取set的key:{}异常", key);
            return 0;
        }
    }

    /**
     * 移除值为value
     *
     * @param key
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long removeSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().remove(key, values);
        } catch (Exception e) {
            log.error("删除set的key:{}异常", key);
            return 0;
        }
    }


}
